<?php


namespace EchoPHP\Console;


use EchoPHP\Foundation\Container;
use EchoPHP\Support\Str;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Command extends SymfonyCommand {

    /**
     * 应用程序容器
     * @var Container
     */
    protected $container;
    /**
     * 脚本命令签名，标识使用的命令及相关参数  命令名称 命令参数 选项
     * @var
     */
    protected $signature;
    /**
     * 脚本说明
     * @var
     */
    protected $description;
    /**
     * 脚本命令名称
     * @var
     */
    protected $name;
    /**
     * 请求参数
     * @var InputInterface
     */
    protected $input;
    /**
     * 输出结果
     * @var OutputStyle
     */
    protected $output;
    /**
     * 输出信息冗长级别
     * @var
     */
    protected $verbosity = OutputInterface::VERBOSITY_NORMAL;

    /**
     * The mapping between human readable verbosity levels and Symfony's OutputInterface.
     *
     * @var array
     */
    protected $verbosityMap = [
        'v' => OutputInterface::VERBOSITY_VERBOSE,
        'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE,
        'vvv' => OutputInterface::VERBOSITY_DEBUG,
        'quiet' => OutputInterface::VERBOSITY_QUIET,
        'normal' => OutputInterface::VERBOSITY_NORMAL,
    ];

    public function __construct() {
        $this->parseSignature();

        $this->setDescription($this->description);
    }

    /**
     * 解析签名
     */
    protected function parseSignature() {
        [$name, $arguments, $options] = Parser::parse($this->signature);

        parent::__construct($this->name = $name);

        $this->getDefinition()->addArguments($arguments);
        $this->getDefinition()->addOptions($options);
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     */
    public function setIOInterface(InputInterface $input = null, OutputInterface $output = null) {
        if (!empty($input)) $this->input = $input;
        if (!empty($output)) $this->output = $output;
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @throws \Exception
     */
    public function run(InputInterface $input, OutputInterface $output) {
        $this->output = $this->container->make(
            OutputStyle::class, ['input' => $input, 'output' => $output]
        );

        return parent::run(
            $this->input = $input, $this->output
        );
    }


    /**
     * Execute the console command.
     *
     * @param \Symfony\Component\Console\Input\InputInterface $input
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @return mixed
     */
    protected function execute(InputInterface $input, OutputInterface $output) {
        return call_user_func([$this, 'handle']);
    }

    /**
     * Determine if the given argument is present.
     *
     * @param string|int $name
     * @return bool
     */
    public function hasArgument($name) {
        return $this->input->hasArgument($name);
    }

    /**
     * Get the value of a command argument.
     *
     * @param string|null $key
     * @return string|array|null
     */
    public function argument($key = null) {
        if (is_null($key)) {
            return $this->input->getArguments();
        }

        return $this->input->getArgument($key);
    }

    /**
     * Get all of the arguments passed to the command.
     *
     * @return array
     */
    public function arguments() {
        return $this->argument();
    }

    /**
     * Write a string as standard output.
     *
     * @param string $string
     * @param string $style
     * @param int|string|null $verbosity
     * @return void
     */
    public function line($string, $style = null, $verbosity = null) {
        $styled = $style ? "<$style>$string</$style>" : $string;

        $this->output->writeln($styled, $this->parseVerbosity($verbosity));
    }

    /**
     * Set the verbosity level.
     *
     * @param string|int $level
     * @return void
     */
    protected function setVerbosity($level) {
        $this->verbosity = $this->parseVerbosity($level);
    }

    /**
     * Get the verbosity level in terms of Symfony's OutputInterface level.
     *
     * @param string|int|null $level
     * @return int
     */
    protected function parseVerbosity($level = null) {
        if (isset($this->verbosityMap[$level])) {
            $level = $this->verbosityMap[$level];
        } elseif (!is_int($level)) {
            $level = $this->verbosity;
        }

        return $level;
    }

    /**
     * Write a string as information output.
     *
     * @param string $string
     * @param int|string|null $verbosity
     * @return void
     */
    public function info($string, $verbosity = null) {
        $this->line($string, 'info', $verbosity);
    }

    /**
     * Write a string as comment output.
     *
     * @param string $string
     * @param int|string|null $verbosity
     * @return void
     */
    public function comment($string, $verbosity = null) {
        $this->line($string, 'comment', $verbosity);
    }

    /**
     * Write a string as question output.
     *
     * @param string $string
     * @param int|string|null $verbosity
     * @return void
     */
    public function question($string, $verbosity = null) {
        $this->line($string, 'question', $verbosity);
    }

    /**
     * Write a string as error output.
     *
     * @param string $string
     * @param int|string|null $verbosity
     * @return void
     */
    public function error($string, $verbosity = null) {
        $this->line($string, 'error', $verbosity);
    }

    /**
     * Write a string as warning output.
     *
     * @param string $string
     * @param int|string|null $verbosity
     * @return void
     */
    public function warn($string, $verbosity = null) {
        if (!$this->output->getFormatter()->hasStyle('warning')) {
            $style = new OutputFormatterStyle('yellow');

            $this->output->getFormatter()->setStyle('warning', $style);
        }

        $this->line($string, 'warning', $verbosity);
    }

    /**
     * Write a string in an alert box.
     *
     * @param string $string
     * @return void
     */
    public function alert($string) {
        $length = Str::length(strip_tags($string)) + 12;

        $this->comment(str_repeat('*', $length));
        $this->comment('*     ' . $string . '     *');
        $this->comment(str_repeat('*', $length));

        $this->output->newLine();
    }

    public function setContainer(Container $container) {
        $this->container = $container;
    }

    public function getContainer() {
        return $this->container;
    }

}